C 语言系统 UML 梳理
C 语言由 Dennis Ritchie 于 1969–1973 年在贝尔实验室设计开发,是一种通用的、过程式的编程语言。C 是系统编程的事实标准——Unix/Linux 内核、嵌入式固件、数据库引擎、编程语言运行时均以 C 编写。本文档从行为与结构两个维度,用 UML 图梳理 C 语言的编译系统、运行时和使用者视角。
1. C 语言概述
C 语言是编译型、静态类型的过程式编程语言,设计哲学是"信任程序员"——提供对硬件的直接访问(指针、位操作),不做运行时检查(如数组越界),极高效率。
核心特性
- 编译型:源码需经过编译才可执行
- 静态类型:编译时确定类型
- 过程式:以函数为基本组织单位
- 指针:直接操作内存地址
- 预处理器:宏、条件编译、文件包含
- 标准库精简:约 29 个标准头文件
典型应用领域
- 操作系统内核(Linux、Windows、macOS)
- 嵌入式系统(汽车 ECU、IoT 设备)
- 数据库系统(MySQL、PostgreSQL、SQLite)
- 编程语言解释器/运行时(Python、Ruby)
- 高性能计算(科学计算、图形引擎)
2. 行为图(系统做什么)
从使用者视角出发,逐步细化 C 语言编译系统与运行时行为。
2.1 用例图
展示 C 语言的不同使用者及其典型操作场景。
C 语言使用者与用例
2.2 活动图:编译流程
C 源码到可执行文件的四阶段管道(Pipeline):预处理 → 编译 → 汇编 → 链接。
C 编译四阶段活动图(带泳道)
2.3 序列图:编译管道完整流程
以一个多文件项目为例,展示从源文件到最终可执行文件的完整交互序列。
多文件编译序列图
2.4 状态机图:C 程序生命周期
描述一个 C 程序从源代码到运行结束的完整生命周期状态。
C 程序生命周期状态机
3. 结构图(系统由什么组成)
从宏观架构到微观细节,拆解 C 语言的静态结构。
3.1 组件图:C 生态系统
展示 C 开发环境的完整生态组件及其依赖关系。
C 开发生态系统组件图
3.2 包图:C 标准库结构
C 标准库(libc)的分类组织——头文件按功能划分为不同模块。
C 标准库头文件包图
3.3 C 程序内存布局
展示 C 程序运行时的虚拟内存地址空间布局(Text / Data / BSS / Heap / Stack)及对应数据存放位置。
C 程序运行时内存布局
内存段说明:Text 段存放指令(只读);Data 段存放已初始化全局变量;BSS 段存放未初始化全局变量(启动时零填充);Heap 向上增长,由 malloc/free 管理;Stack 向下增长,存放函数调用帧和局部变量。
3.4 部署图:构建环境
展示一个典型的 C 项目跨平台开发与持续集成部署拓扑。
C 项目构建与 CI 部署图
4. 使用手册
4.1 基本语法
Hello World
#include <stdio.h>
int main(void) {
printf("Hello, World!\n");
return 0;
}
变量与基本类型
| 类型 | 字节(通常) | 说明 | 示例 |
|---|---|---|---|
char | 1 | 字符/小整数 | char c = 'A'; |
short | 2 | 短整数 | short s = 32767; |
int | 4 | 整数 | int i = 42; |
long | 4/8 | 长整数 | long l = 100000L; |
long long | 8 | 长长整数 | long long ll = 9999999LL; |
float | 4 | 单精度浮点 | float f = 3.14f; |
double | 8 | 双精度浮点 | double d = 3.1415926; |
_Bool | 1 | 布尔(C99) | _Bool flag = 1; |
控制流
// if-else
if (x > 0) {
printf("positive\n");
} else if (x < 0) {
printf("negative\n");
} else {
printf("zero\n");
}
// switch
switch (n) {
case 1: printf("one\n"); break;
case 2: printf("two\n"); break;
default: printf("other\n"); break;
}
// for 循环
for (int i = 0; i < 10; i++) {
printf("%d\n", i);
}
// while 循环
while (n > 0) {
n--;
}
// do-while
do {
n++;
} while (n < 100);
4.2 指针与内存
指针是 C 语言最强大也最危险的特征——直接操作内存地址。
int x = 42;
int *p = &x; // p 指向 x 的地址
printf("%d\n", *p); // 解引用:输出 42
// 动态内存分配
int *arr = (int *)malloc(10 * sizeof(int));
if (arr == NULL) {
perror("malloc failed");
return 1;
}
for (int i = 0; i < 10; i++) arr[i] = i * i;
free(arr); // 必须手动释放
// 函数指针
int add(int a, int b) { return a + b; }
int (*func_ptr)(int, int) = &add;
printf("%d\n", func_ptr(3, 4)); // 输出 7
// void 指针(泛型指针)
void *vp = &x;
int *ip = (int *)vp; // 需显式转换
指针与数组
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // 数组名退化为指针
// p[2] 等价于 *(p + 2),输出 3
// 指针算术
for (int *q = arr; q < arr + 5; q++) {
printf("%d ", *q);
}
4.3 结构体与联合体
// 结构体
struct Point {
int x;
int y;
};
struct Point p1 = {10, 20};
struct Point *pp = &p1;
pp->x = 30; // 箭头操作符
// 联合体(所有成员共享同一内存)
union Data {
int i;
float f;
char str[20];
};
// 枚举
enum Color { RED = 0, GREEN, BLUE };
enum Color c = GREEN;
// typedef 别名
typedef struct {
char name[32];
int age;
} Person;
Person p2 = {"Alice", 25};
4.4 预处理器
预处理器在编译前对源码进行文本替换,由 # 开头的指令控制。
// 宏定义
#define PI 3.14159
#define SQUARE(x) ((x) * (x)) // 注意括号:避免展开歧义
// 条件编译
#ifdef DEBUG
printf("Debug mode: x=%d\n", x);
#endif
#if defined(__linux__)
// Linux 特定代码
#elif defined(_WIN32)
// Windows 特定代码
#endif
// 头文件卫士(防止重复包含)
#ifndef MYHEADER_H
#define MYHEADER_H
// 头文件内容
#endif
// C++ 兼容
#ifdef __cplusplus
extern "C" {
#endif
// C 函数声明
#ifdef __cplusplus
}
#endif
4.5 编译工具链
GCC 常用命令
# 单文件编译
gcc hello.c -o hello
# 分步编译
gcc -E hello.c -o hello.i # 仅预处理
gcc -S hello.i -o hello.s # 编译为汇编
gcc -c hello.s -o hello.o # 汇编为目标文件
gcc hello.o -o hello # 链接
# 多文件编译
gcc -c foo.c -o foo.o
gcc -c bar.c -o bar.o
gcc foo.o bar.o -o program
# 常用选项
gcc -Wall -Wextra -g -O2 main.c -o main # 警告全开 + 调试 + 优化
gcc -std=c11 main.c -o main # 指定 C11 标准
gcc -I./include -L./lib -lmylib main.c # 指定头文件/库路径
静态库与动态库
# 静态库(.a / .lib)
gcc -c mylib.c -o mylib.o
ar rcs libmylib.a mylib.o
gcc main.c -L. -lmylib -o program
# 动态库(.so / .dylib / .dll)
gcc -shared -fPIC mylib.c -o libmylib.so
gcc main.c -L. -lmylib -o program
# macOS 动态库
gcc -dynamiclib mylib.c -o libmylib.dylib
调试工具
# GDB
gdb ./program # 启动调试
(gdb) break main # 在 main 设断点
(gdb) run arg1 arg2 # 运行
(gdb) next # 单步(不进入函数)
(gdb) step # 单步(进入函数)
(gdb) print x # 打印变量
(gdb) backtrace # 查看调用栈
(gdb) watch y # 监视变量变化
# Valgrind(内存泄漏检测)
valgrind --leak-check=full ./program
# Address Sanitizer
gcc -fsanitize=address -g main.c -o main
./main
4.6 构建系统
Makefile
CC = gcc
CFLAGS = -Wall -Wextra -g -O2
LDFLAGS =
TARGET = program
OBJS = main.o foo.o bar.o
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
.PHONY: clean
clean:
rm -f $(OBJS) $(TARGET)
CMake (CMakeLists.txt)
cmake_minimum_required(VERSION 3.10)
project(MyProject C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
add_executable(program main.c foo.c bar.c)
target_include_directories(program PRIVATE include)
target_link_libraries(program PRIVATE m) # 链接 libm
5. 语言标准演进
| 年份 | 标准 | 别名 | 主要新增 |
|---|---|---|---|
| 1972 | — | K&R C | 原始版本,Dennis Ritchie 设计 |
| 1989 | C89 / ANSI C | ANSI X3.159-1989 | 函数原型、void、标准库、const/volatile |
| 1990 | C90 | ISO/IEC 9899:1990 | ISO 采纳的 C89,基本一致 |
| 1999 | C99 | ISO/IEC 9899:1999 | 内联函数、变长数组、// 注释、long long、_Bool、<stdint.h> |
| 2011 | C11 | ISO/IEC 9899:2011 | _Generic、_Atomic、_Thread_local、匿名 struct/union、bounds-checking 接口(可选) |
| 2018 | C17 | ISO/IEC 9899:2018 | 缺陷修复版本,无新特性 |
| 2024 | C23 | ISO/IEC 9899:2024 | #embed、nullptr 常量、typeof/typeof_unqual、十进制浮点、constexpr、{} 空初始化器 |